package com.zxy.config.shiro;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zxy.common.result.Result;
import com.zxy.config.util.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 在配置类配置好了之后，我们来配置我们的jwtFilter，因为需要替换掉原来shiro自带的filter
 *
 * @author: Zxy
 * @Date: 2021-04-15
 **/
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtFilter extends AuthenticatingFilter {

    private final JwtUtils jwtUtils;

    /*
    这个方法并不是字面意思的根据用户生成token，而是去获取到请求头中的token，交给我们的自定义Realm去进行认证使用
    这儿获取到的token需要我们用一个自定义token类去接收到，这个类需要去实现AuthenticationToken类才能将获取到的token转给Realm
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if (StringUtils.isEmpty(jwt)) {
            return null;
        }
        return new JwtToken(jwt);
    }

    /*
    这个方法就是进行认证的方法，用来判断用户的token是否过期，是否失效，是否和生成的token对比不上
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        log.info("onAccessDenied方法执行");
        // 判断用户请求的token
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        // 如果token是空的，我们可以直接返回true，进入到controller，就当作是游客用户
        if (StringUtils.isEmpty(jwt)) {
            return true;
        } else {
            // 校验jwt
            Claims claims = jwtUtils.getClaimByToken(jwt);
            if (claims == null || jwtUtils.isTokenExpired(claims.getExpiration())) {
                throw new ExpiredCredentialsException("token已失效，请重新登录");
            }
            // 执行登录
            return executeLogin(servletRequest, servletResponse);
        }
    }

    // 执行登录如果出现异常就会来到这个方法
    @SneakyThrows
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        Throwable throwable = e.getCause() == null ? e : e.getCause();
        Result result = Result.fail(throwable.getMessage());
        String json = JSONUtil.toJsonStr(result);
        httpServletResponse.getWriter().println(json);
        return false;
    }
}
